/****************************************************
          File Name:39-1.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Sat 13 Sep 2014 07:19:16 PM CST
*****************************************************/

// gcc 39-1.c -lcap
/*
 * tested on openSUSE linux-3.16.2 
 * NOT work if not run as root or set-user-id-root
 * ps: I don't know the kernel in openSUSE tumbleweed branch
 * was compiled with linux capability or POSIX capability :(
 */
#include<stdio.h>
#include<string.h>
#include<sched.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/capability.h>

void err(int eRR, char *fun)
{
    if(eRR==-1)
    {
        perror(fun);
        exit(errno);
    }
}

/******************* capability code start ********************/

static int modifycap(int capability, int setting)
{
	cap_t caps;
	cap_value_t caplist[1];
	
	/** get current capability (a copy) from /proc **/
	caps=cap_get_proc();
	if(caps==NULL)
		return -1;
	/*
	 * change the capability, make this normal user 
	 * can run soomthly
	 */
	caplist[0]=capability;
	if(cap_set_flag(caps,CAP_EFFECTIVE,1,caplist,setting)==-1)
	{
		cap_free(caps);
		return -1;
	}
	/** set the changed capability **/
	if(cap_set_proc(caps)==-1)
	{
		cap_free(caps);
		return -1;
	}
	/** free the capability copy **/
	if(cap_free(caps)==-1)
		return -1;
	
	return 0;
}

static int raisecap(int capability)
{
	return modifycap(capability,CAP_SET);
}

static int dropallcaps(void)
{
	cap_t empty;
	int s;
	
	empty=cap_init();
	if(empty==NULL)
		return -1;
	
	s=cap_set_proc(empty);
	if(cap_free(empty)==-1)
		return -1;
	
	return s;
}

/******************* capability code end *******************/


int main(int argc,char *argv[])
{
    pid_t pid;
    int res;
    struct sched_param pa;

    if(argc<2 || strcmp(argv[1],"--help")==0)
    {
        fprintf(stderr,"%s %s\n",argv[0],"-[set|get] (policy)[r|f|o] (priority)[1~99] pid ...  ");
        printf("schedulers:\n");
        printf("SCHED_RR: %d\nSCHED_FIFO: %d\nSCHED_OTHER: %d\n",SCHED_RR,SCHED_FIFO,SCHED_OTHER);
        printf("************************************\n");
        exit(1);
    }

    if(strcmp(argv[1],"-set")==0)
    {
/*** add capability code here ***/
		int status;
		if(status=raisecap(CAP_SETUID))
			err(status,"raisecap");
/*********************************/
        pid=atoi(argv[4]);
        pa.sched_priority=atoi(argv[3]);

        switch(argv[2][0])
        {
            case 'r':
                res=sched_setscheduler(pid,SCHED_RR,&pa);
                err(res,"setschedduler");
                break;
            case 'f':
                res=sched_setscheduler(pid,SCHED_FIFO,&pa);
                err(res,"setschedduler");
                break;
            case 'o':
                res=sched_setscheduler(pid,SCHED_OTHER,&pa);
                err(res,"setschedduler");
                break;
        }
 /*** add capability drop code here ***/
		if(status=dropallcaps())
			err(status,"drop capability");
/*************************************/
    }
    else if(strcmp(argv[1],"-get")==0)
    {
       pid=atoi(argv[2]);
       res=sched_getscheduler(pid); 
       sched_getparam(pid,&pa);
       printf("the sched is: %s\n policy is: %d\n",res==0?"OTHER":(res==1)?"FIFO":"RR"
               ,pa.sched_priority);
    }
    exit(0);
}
